This patch adds backward-compatibility support for the --xattrs option.
Since the main release has never had xattr support, the trunk doesn't
need this code.  If you want to make rsync 3.0.x communicate with an
older (patched) release, use this.

To use this patch, run these commands for a successful build:

    patch -p1 <patches/acls.diff
    patch -p1 <patches/xattrs.diff
    ./configure                         (optional if already run)
    make

based-on: patch/b3.0.x/acls
diff --git a/compat.c b/compat.c
--- a/compat.c
+++ b/compat.c
@@ -193,13 +193,6 @@ void setup_protocol(int f_out,int f_in)
 	if (protocol_version < 30) {
 		if (append_mode == 1)
 			append_mode = 2;
-		if (preserve_xattrs && !local_server) {
-			rprintf(FERROR,
-			    "--xattrs requires protocol 30 or higher"
-			    " (negotiated %d).\n",
-			    protocol_version);
-			exit_cleanup(RERR_PROTOCOL);
-		}
 	}
 
 	if (delete_mode && !(delete_before+delete_during+delete_after)) {
diff --git a/xattrs.c b/xattrs.c
--- a/xattrs.c
+++ b/xattrs.c
@@ -21,6 +21,7 @@
 
 #include "rsync.h"
 #include "ifuncs.h"
+#include "io.h"
 #include "lib/sysxattrs.h"
 
 #ifdef SUPPORT_XATTRS
@@ -36,6 +37,7 @@ extern int preserve_links;
 extern int preserve_devices;
 extern int preserve_specials;
 extern int checksum_seed;
+extern int protocol_version;
 
 #define RSYNC_XAL_INITIAL 5
 #define RSYNC_XAL_LIST_INITIAL 100
@@ -251,7 +253,7 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
 		if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
 			return -1;
 
-		if (datum_len > MAX_FULL_DATUM) {
+		if (datum_len > MAX_FULL_DATUM && protocol_version >= 30) {
 			/* For large datums, we store a flag and a checksum. */
 			name_offset = 1 + MAX_DIGEST_LEN;
 			sum_init(checksum_seed);
@@ -373,7 +375,7 @@ static int find_matching_xattr(item_list *xalp)
 			 || rxas1[j].datum_len != rxas2[j].datum_len
 			 || strcmp(rxas1[j].name, rxas2[j].name))
 				break;
-			if (rxas1[j].datum_len > MAX_FULL_DATUM) {
+			if (rxas1[j].datum_len > MAX_FULL_DATUM && protocol_version >= 30) {
 				if (memcmp(rxas1[j].datum + 1,
 					   rxas2[j].datum + 1,
 					   MAX_DIGEST_LEN) != 0)
@@ -410,13 +412,22 @@ int send_xattr(int f, stat_x *sxp)
 {
 	int ndx = find_matching_xattr(sxp->xattr);
 
-	/* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
-	write_varint(f, ndx + 1);
+	if (protocol_version < 30) {
+		if (ndx < 0)
+			write_byte(f, 'X');
+		else {
+			write_byte(f, 'x');
+			write_int(f, ndx);
+		}
+	} else {
+		/* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
+		write_varint(f, ndx + 1);
+	}
 
 	if (ndx < 0) {
 		rsync_xa *rxa;
 		int count = sxp->xattr->count;
-		write_varint(f, count);
+		write_varint30(f, count);
 		for (rxa = sxp->xattr->items; count--; rxa++) {
 			size_t name_len = rxa->name_len;
 			const char *name = rxa->name;
@@ -435,8 +446,8 @@ int send_xattr(int f, stat_x *sxp)
 				name_len += UPRE_LEN;
 			}
 #endif
-			write_varint(f, name_len);
-			write_varint(f, rxa->datum_len);
+			write_varint30(f, name_len);
+			write_varint30(f, rxa->datum_len);
 #ifndef HAVE_LINUX_XATTRS
 			if (name_len > rxa->name_len) {
 				write_buf(f, USER_PREFIX, UPRE_LEN);
@@ -444,7 +455,7 @@ int send_xattr(int f, stat_x *sxp)
 			}
 #endif
 			write_buf(f, name, name_len);
-			if (rxa->datum_len > MAX_FULL_DATUM)
+			if (rxa->datum_len > MAX_FULL_DATUM && protocol_version >= 30)
 				write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN);
 			else
 				write_buf(f, rxa->datum, rxa->datum_len);
@@ -494,7 +505,7 @@ int xattr_diff(struct file_struct *file, stat_x *sxp, int find_all)
 		cmp = rec_cnt ? strcmp(snd_rxa->name, rec_rxa->name) : -1;
 		if (cmp > 0)
 			same = 0;
-		else if (snd_rxa->datum_len > MAX_FULL_DATUM) {
+		else if (snd_rxa->datum_len > MAX_FULL_DATUM && protocol_version >= 30) {
 			same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
 			    && memcmp(snd_rxa->datum + 1, rec_rxa->datum + 1,
 				      MAX_DIGEST_LEN) == 0;
@@ -539,6 +550,9 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
 	int cnt, prior_req = 0;
 	rsync_xa *rxa;
 
+	if (protocol_version < 30)
+		return;
+
 	lst += F_XATTR(file);
 	for (rxa = lst->items, cnt = lst->count; cnt--; rxa++) {
 		if (rxa->datum_len <= MAX_FULL_DATUM)
@@ -595,6 +609,9 @@ int recv_xattr_request(struct file_struct *file, int f_in)
 	rsync_xa *rxa;
 	int rel_pos, cnt, num, got_xattr_data = 0;
 
+	if (protocol_version < 30)
+		return 0;
+
 	if (F_XATTR(file) < 0) {
 		rprintf(FERROR, "recv_xattr_request: internal data error!\n");
 		exit_cleanup(RERR_PROTOCOL);
@@ -657,7 +674,22 @@ void receive_xattr(int f, struct file_struct *file)
 #else
 	int need_sort = 1;
 #endif
-	int ndx = read_varint(f);
+	int ndx;
+
+	if (protocol_version >= 30)
+		ndx = read_varint(f);
+	else {
+		int tag = read_byte(f);
+		if (tag == 'x')
+			ndx = read_int(f) + 1;
+		else if (tag == 'X')
+			ndx = 0;
+		else {
+			rprintf(FERROR, "receive_xattr: unknown extended attribute"
+				" type tag (%02x) for %s\n", tag, f_name(file, NULL));
+			exit_cleanup(RERR_STREAMIO);
+		}
+	}
 
 	if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) {
 		rprintf(FERROR, "receive_xattr: xa index %d out of"
@@ -670,7 +702,7 @@ void receive_xattr(int f, struct file_struct *file)
 		return;
 	}
 
-	if ((count = read_varint(f)) != 0) {
+	if ((count = read_varint30(f)) != 0) {
 		(void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
 		temp_xattr.count = 0;
 	}
@@ -678,9 +710,10 @@ void receive_xattr(int f, struct file_struct *file)
 	for (num = 1; num <= count; num++) {
 		char *ptr, *name;
 		rsync_xa *rxa;
-		size_t name_len = read_varint(f);
-		size_t datum_len = read_varint(f);
-		size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
+		size_t name_len = read_varint30(f);
+		size_t datum_len = read_varint30(f);
+		size_t dget_len = datum_len > MAX_FULL_DATUM && protocol_version >= 30
+				? 1 + MAX_DIGEST_LEN : datum_len;
 		size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
 		if ((dget_len + extra_len < dget_len)
 		 || (dget_len + extra_len + name_len < dget_len))
